package com.stark.commons.spring.web.handler;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

import org.apache.catalina.connector.ClientAbortException;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.stark.commons.lang.util.Utils;

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;

/**
 * 全局异常处理器。
 * <p>业务系统可实现 {@link com.stark.commons.spring.web.support.exception.ExceptionHandler} 接口扩展全局异常处理。
 * @author Ben
 * @since 1.0.0
 * @version 1.0.0
 */
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
	
	@Autowired(required = false)
	private List<com.stark.commons.spring.web.support.exception.ExceptionHandler> exceptionHandlerList;
	
	/**
	 * 全局异常处理，根据不同的异常类型，返回不同的状态码和错误信息。
	 * @param ex 异常。
	 * @return 状态码、错误信息。
	 * @throws JsonProcessingException
	 */
	@ExceptionHandler(Exception.class)
	public ResponseEntity<String> handleException(Exception ex) throws JsonProcessingException {
		ResponseEntity<String> responseEntity = null;
		if (CollectionUtils.isNotEmpty(exceptionHandlerList)) {
			for (com.stark.commons.spring.web.support.exception.ExceptionHandler exceptionHandler : exceptionHandlerList) {
				if (exceptionHandler.instanceofException(ex)) {
					responseEntity = exceptionHandler.handle(ex);
					break;
				}
			}
		}
		if (responseEntity == null) {
			// 客户端取消请求
			if (ex instanceof ClientAbortException) {
				if (log.isInfoEnabled()) {
					log.info("客户端取消请求", ex);
				} else {
					System.out.println("客户端取消请求");
					ex.printStackTrace();
				}
				responseEntity = ResponseEntity.status(499).body(ex.toString());
			}
			// 未知异常处理
			else {
				log.error("捕获到未知异常", ex);
				responseEntity = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ex.toString());
			}
		} else if (responseEntity.getStatusCodeValue() >= 500) {
			log.error(responseEntity.getBody(), ex);
		}
		
		ResponseBody responseBody = new ResponseBody()
				.setStatus(responseEntity.getStatusCode().value())
				.setError(responseEntity.getStatusCode().name())
				.setMessage(responseEntity.getBody())
				.setTrace(Utils.getExceptionStackTrace(ex));
		String body = new ObjectMapper().writeValueAsString(responseBody);
		return ResponseEntity.status(responseEntity.getStatusCode()).body(body);
	}
	
	@Data
	@NoArgsConstructor
	@Accessors(chain = true)
	class ResponseBody implements Serializable {
		
		private static final long serialVersionUID = 3179479301457866713L;

		@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
		private Date timestamp = new Date();
		
		private Integer status;
		
		private String error;
		
		private String message;
		
		private String trace;
		
	}
	
}
